msg_tool\scripts\softpal\img\pgd/
pgd3.rs1use super::base::*;
2use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::img::*;
6use crate::utils::struct_pack::*;
7use anyhow::Result;
8use std::io::{Read, Seek};
9
10#[derive(Debug)]
11pub struct Pgd3Builder {}
12
13impl Pgd3Builder {
14 pub fn new() -> Self {
15 Self {}
16 }
17}
18
19impl ScriptBuilder for Pgd3Builder {
20 fn default_encoding(&self) -> Encoding {
21 Encoding::Cp932
22 }
23
24 fn build_script(
25 &self,
26 buf: Vec<u8>,
27 filename: &str,
28 encoding: Encoding,
29 _archive_encoding: Encoding,
30 config: &ExtraConfig,
31 archive: Option<&Box<dyn Script>>,
32 ) -> Result<Box<dyn Script>> {
33 Ok(Box::new(Pgd3::new(
34 MemReader::new(buf),
35 filename,
36 encoding,
37 config,
38 archive,
39 )?))
40 }
41
42 fn extensions(&self) -> &'static [&'static str] {
43 &["pgd"]
44 }
45
46 fn script_type(&self) -> &'static ScriptType {
47 &ScriptType::SoftpalPgd3
48 }
49
50 fn is_image(&self) -> bool {
51 true
52 }
53
54 fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
55 if buf_len >= 4 && (buf.starts_with(b"PGD3") || buf.starts_with(b"PGD2")) {
56 return Some(20);
57 }
58 None
59 }
60}
61
62#[derive(Debug)]
63pub struct Pgd3 {
64 header: PgdDiffHeader,
65 base_header: PgdGeHeader,
66 base: ImageData,
67 diff: ImageData,
68 fake_compress: bool,
69}
70
71impl Pgd3 {
72 pub fn new<R: Read + Seek>(
73 mut reader: R,
74 filename: &str,
75 encoding: Encoding,
76 config: &ExtraConfig,
77 archive: Option<&Box<dyn Script>>,
78 ) -> Result<Self> {
79 let mut sig = [0u8; 4];
80 reader.read_exact(&mut sig)?;
81 if &sig != b"PGD3" && &sig != b"PGD2" {
82 return Err(anyhow::anyhow!("Not a valid PGD3/PGD2 file"));
83 }
84 let header = PgdDiffHeader::unpack(&mut reader, false, encoding, &None)?;
85 let diff = PgdReader::with_diff_header(reader, &header)?.unpack_overlay()?;
86 let base: Vec<u8> = if let Some(archive) = archive {
87 let mut file = archive.open_file_by_name(&header.base_name, true)?;
88 file.data()?
89 } else {
90 let path = {
91 let mut pb = std::path::PathBuf::from(filename);
92 pb.set_file_name(&header.base_name);
93 crate::utils::files::get_ignorecase_path(&pb)?
94 };
95 crate::utils::files::read_file(&path).map_err(|e| {
96 anyhow::anyhow!("Failed to read base image file '{}': {}", path.display(), e)
97 })?
98 };
99 let mut reader = MemReader::new(base);
100 reader.read_exact(&mut sig)?;
101 if &sig != b"GE \0" {
102 return Err(anyhow::anyhow!(
103 "Base image file '{}' is not a valid GE file",
104 header.base_name
105 ));
106 }
107 let base_header = PgdGeHeader::unpack(&mut reader, false, encoding, &None)?;
108 let base = PgdReader::with_ge_header(reader, &base_header)?.unpack_ge()?;
109 Ok(Self {
110 header,
111 base_header,
112 base,
113 diff,
114 fake_compress: config.pgd_fake_compress,
115 })
116 }
117}
118
119impl Script for Pgd3 {
120 fn default_output_script_type(&self) -> OutputScriptType {
121 OutputScriptType::Json
122 }
123
124 fn default_format_type(&self) -> FormatOptions {
125 FormatOptions::None
126 }
127
128 fn is_image(&self) -> bool {
129 true
130 }
131
132 fn export_image(&self) -> Result<ImageData> {
133 let mut base = if self.base_header.is_base_file() {
134 self.base.clone()
135 } else {
136 draw_on_canvas(
137 self.base.clone(),
138 self.base_header.canvas_width,
139 self.base_header.canvas_height,
140 self.base_header.offset_x,
141 self.base_header.offset_y,
142 )?
143 };
144 draw_on_img(
145 &mut base,
146 &self.diff,
147 self.header.offset_x as u32,
148 self.header.offset_y as u32,
149 )?;
150 Ok(base)
151 }
152
153 fn import_image<'a>(
154 &'a self,
155 data: ImageData,
156 _filename: &str,
157 mut file: Box<dyn WriteSeek + 'a>,
158 ) -> Result<()> {
159 let mut header = PgdGeHeader {
160 offset_x: self.base_header.offset_x,
161 offset_y: self.base_header.offset_y,
162 width: self.base_header.width,
163 height: self.base_header.height,
164 canvas_height: self.base_header.canvas_height,
165 canvas_width: self.base_header.canvas_width,
166 mode: self.base_header.mode,
167 _unk: self.base_header._unk,
168 };
169 if data.height != header.height {
170 return Err(anyhow::anyhow!(
171 "Image height does not match: expected {}, got {}",
172 header.height,
173 data.height
174 ));
175 }
176 if data.width != header.width {
177 return Err(anyhow::anyhow!(
178 "Image width does not match: expected {}, got {}",
179 header.width,
180 data.width
181 ));
182 }
183 header.mode = 3;
184 file.write_all(b"GE \0")?;
185 header.pack(&mut file, false, Encoding::Utf8, &None)?;
186 PgdWriter::new(data, self.fake_compress)
187 .with_method(3)
188 .pack_ge(&mut file)?;
189 Ok(())
190 }
191}
192
193fn draw_on_img(base: &mut ImageData, diff: &ImageData, left: u32, top: u32) -> Result<()> {
194 if base.color_type != diff.color_type {
195 return Err(anyhow::anyhow!(
196 "Color types do not match: {:?} vs {:?}",
197 base.color_type,
198 diff.color_type
199 ));
200 }
201 let bpp = base.color_type.bpp(1) as usize;
202 let base_stride = base.width as usize * bpp;
203 let diff_stride = diff.width as usize * bpp;
204
205 for y in 0..diff.height {
206 let base_y = top + y;
207 if base_y >= base.height {
208 continue; }
210
211 for x in 0..diff.width {
212 let base_x = left + x;
213 if base_x >= base.width {
214 continue; }
216
217 let base_index = (base_y as usize * base_stride) + (base_x as usize * bpp);
218 let diff_index = (y as usize * diff_stride) + (x as usize * bpp);
219
220 let diff_pixel = &diff.data[diff_index..diff_index + bpp];
221 let base_pixel_orig = base.data[base_index..base_index + bpp].to_vec();
222 let mut b = base_pixel_orig[0];
223 let mut g = base_pixel_orig[1];
224 let mut r = base_pixel_orig[2];
225 b ^= diff_pixel[0];
226 g ^= diff_pixel[1];
227 r ^= diff_pixel[2];
228 base.data[base_index] = b;
229 base.data[base_index + 1] = g;
230 base.data[base_index + 2] = r;
231 if bpp == 4 {
232 let mut a = base_pixel_orig[3];
233 a ^= diff_pixel[3];
234 base.data[base_index + 3] = a;
235 }
236 }
237 }
238 Ok(())
239}